package online.chazi.bootplus.autoconfiguation.multiple.redis;

import online.chazi.bootplus.multiple.redis.MultipleRedisManager;
import online.chazi.bootplus.multiple.redis.MultipleRedisProperties;
import online.chazi.bootplus.multiple.redis.MultipleRedisTemplateBuilder;
import online.chazi.bootplus.multiple.redis.NamedRedisProperties;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * 说明：作者很懒，什么都没留下
 * Created by 叉子同学 on 2020-03-04 14:44
 */
@Configuration
@EnableConfigurationProperties(MultipleRedisProperties.class)
public class MultipleRedisConfig<K, V> {

    @Resource
    private MultipleRedisTemplateBuilder<K, V> multipleRedisTemplateBuilder;

    @Component
    public static class DefaultMultipleRedisTemplateBuilder<K, V> implements MultipleRedisTemplateBuilder<K, V> {
        @Override
        public RedisTemplate<K, V> buildRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
            System.out.println("~~~~~~~~~~~ 默认的MultipleRedisTemplateBuilder ~~~~~~~~~~~");
            RedisTemplate<K, V> template = new RedisTemplate<>();
            template.setConnectionFactory(redisConnectionFactory);
            template.setKeySerializer(new StringRedisSerializer());
            template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            template.setHashKeySerializer(new StringRedisSerializer());
            template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
            return template;
        }
    }

    @Bean
    MultipleRedisManager<K, V> multipleRedisManager(MultipleRedisProperties multipleRedisProperties) {
        MultipleRedisManager<K, V> manager = new MultipleRedisManager<>();
        for (NamedRedisProperties config : multipleRedisProperties) {
            if (config.getTimeout() == null) config.setTimeout(Duration.ofSeconds(60));
            if (config.getLettuce().getPool() != null) {
                manager.put(config.getName(), multipleRedisTemplateBuilder.buildRedisTemplate(buildLettuceConnectionFactory(config)));
            } else if (config.getJedis().getPool() != null) {
                //使用jedis
                manager.put(config.getName(), multipleRedisTemplateBuilder.buildRedisTemplate(buildJedisConnectionFactory(config)));
            }
        }
        return manager;
    }

    private RedisConnectionFactory buildJedisConnectionFactory(RedisProperties config) {

        System.out.println("~~~~~~~~~~~ jedis只使用单点连接，请考虑使用云服务商提供的redis实例 ~~~~~~~~~~~");

        JedisClientConfiguration.JedisClientConfigurationBuilder builder = JedisClientConfiguration.builder()
                .readTimeout(config.getTimeout());

        RedisProperties.Pool pool = config.getJedis().getPool();
        if (pool != null) {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            poolConfig.setMaxTotal(pool.getMaxActive());
            poolConfig.setMaxIdle(pool.getMaxIdle());
            poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
            poolConfig.setMinIdle(pool.getMinIdle());
            poolConfig.setTestOnBorrow(true);
            poolConfig.setTestOnReturn(false);
            poolConfig.setTestWhileIdle(true);
            builder.usePooling().poolConfig(poolConfig);
        }

        JedisClientConfiguration clientConfig = builder.build();
        // 单点redis
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        // 哨兵redis
        //RedisSentinelConfiguration redisConfig = new RedisSentinelConfiguration();
        // 集群redis
        //RedisClusterConfiguration redisConfig = new RedisClusterConfiguration();
        redisConfig.setHostName(config.getHost());
        redisConfig.setPassword(RedisPassword.of(config.getPassword()));
        redisConfig.setPort(config.getPort());
        redisConfig.setDatabase(config.getDatabase());
        return new JedisConnectionFactory(redisConfig, clientConfig);
    }

    private RedisConnectionFactory buildLettuceConnectionFactory(RedisProperties config) {

        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setDatabase(config.getDatabase());
        redisStandaloneConfiguration.setHostName(config.getHost());
        redisStandaloneConfiguration.setPort(config.getPort());
        redisStandaloneConfiguration.setPassword(RedisPassword.of(config.getPassword()));

        //redis客户端配置
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
                builder = LettucePoolingClientConfiguration.builder().
                commandTimeout(config.getTimeout()).shutdownTimeout(config.getTimeout());

        RedisProperties.Pool pool = config.getLettuce().getPool();
        if (pool != null) {
            //连接池配置
            GenericObjectPoolConfig<Object> genericObjectPoolConfig = new GenericObjectPoolConfig<>();
            genericObjectPoolConfig.setMaxIdle(pool.getMaxIdle());
            genericObjectPoolConfig.setMinIdle(pool.getMinIdle());
            genericObjectPoolConfig.setMaxTotal(pool.getMaxActive());
            genericObjectPoolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
            builder.poolConfig(genericObjectPoolConfig);
        }

        LettuceClientConfiguration lettuceClientConfiguration = builder.build();

        //根据配置和客户端配置创建连接
        LettuceConnectionFactory lettuceConnectionFactory = new
                LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration);
        lettuceConnectionFactory.afterPropertiesSet();

        return lettuceConnectionFactory;
    }

}
